package com.acmedcare.framework.exchange.center.executer;

import com.acmedcare.framework.boot.snowflake.EnableSnowflake;
import com.acmedcare.framework.exchange.center.core.PluginContainerMBean;
import com.acmedcare.framework.exchange.center.core.PluginContainerMBeanProxy;
import com.acmedcare.framework.exchange.center.core.handler.AgentHandlerMBean;
import com.acmedcare.framework.exchange.center.core.handler.AgentHandlerProxy;
import com.acmedcare.framework.exchange.center.core.handler.PluginHandler;
import com.acmedcare.framework.exchange.center.core.handler.StageCommandHandler;
import com.acmedcare.framework.exchange.center.core.storage.PluginRepository;
import com.acmedcare.framework.exchange.center.core.storage.StageRepository;
import com.acmedcare.framework.exchange.center.entity.bean.Task;
import com.acmedcare.framework.exchange.center.entity.dto.CommandEvent;
import com.acmedcare.framework.exchange.center.entity.dto.Hdfs;
import com.acmedcare.framework.starter.bus.jackson.AcmedcareEventScan;
import com.acmedcare.framework.starter.control.GrpcLauncher;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

@SpringBootApplication(scanBasePackageClasses = {ExecutorApplication.class, PluginHandler.class,
    PluginRepository.class})
@EnableSnowflake
@AcmedcareEventScan(basePackageClasses = {CommandEvent.class, Task.class})
@EnableConfigurationProperties({ExecutorConfig.class, Hdfs.class})
public class ExecutorApplication {

  /**
   * Date格式化字符串
   */
  public static final String DATE_FORMAT = "yyyy-MM-dd";
  /**
   * DateTime格式化字符串
   */
  public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
  /**
   * Time格式化字符串
   */
  public static final String TIME_FORMAT = "HH:mm:ss";

  public static void main(String[] args) {

    ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder()
        .sources(ExecutorApplication.class)
        // default properties
        .web(WebApplicationType.SERVLET)
        .run(args);
    ApplicationReadyEventListener applicationReadyEventListener = applicationContext
        .getBean(ApplicationReadyEventListener.class);
    applicationReadyEventListener.init();

    try {

      GrpcLauncher.startControl(applicationContext);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Bean
  public PluginContainerMBean pluginContainer(Hdfs hdfs) {

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(PluginContainerMBean.class);
    enhancer.setCallback(new PluginContainerMBeanProxy(hdfs));
    return (PluginContainerMBean) enhancer.create();
  }

  @Bean(name = "agentHandler")
  public AgentHandlerMBean agentHandler() {

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(AgentHandlerMBean.class);
    enhancer.setCallback(new AgentHandlerProxy());
    return (AgentHandlerMBean) enhancer.create();
  }

  @Bean
  public StageCommandHandler stageCommandHandler(PluginContainerMBean pluginContainer,
      ObjectMapper objectMapper, StageRepository stageRepository) {

    return new StageCommandHandler(objectMapper, pluginContainer, stageRepository);
  }

  @Bean
  @Primary
  public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    //noinspection Duplicates
    return builder -> builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(
        DateTimeFormatter.ofPattern(DATETIME_FORMAT)))
        .serializerByType(LocalDate.class,
            new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
        .serializerByType(LocalTime.class,
            new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT)))
        .deserializerByType(LocalDateTime.class,
            new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)))
        .deserializerByType(LocalDate.class,
            new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
        .deserializerByType(LocalTime.class,
            new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
  }
}